package com.irdstudio.efp.nls.service.impl.yed.queue;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.irdstudio.basic.framework.core.exception.BizException;
import com.irdstudio.basic.framework.core.exception.HangException;
import com.irdstudio.basic.framework.core.threadpool.AbstractThreadTask;
import com.irdstudio.basic.framework.core.threadpool.ExecuteRtnInfo;
import com.irdstudio.basic.framework.core.util.DateUtility;
import com.irdstudio.basic.framework.core.util.SpringContextUtils;
import com.irdstudio.basic.framework.core.util.TraceUtil;
import com.irdstudio.efp.console.service.facade.PrdAdaptedSoltService;
import com.irdstudio.efp.console.service.vo.PrdAdaptedSoltVO;
import com.irdstudio.efp.esb.common.constant.MsLoanConstant;
import com.irdstudio.efp.loan.service.facade.AccLoanService;
import com.irdstudio.efp.loan.service.facade.LoanRepayDetailService;
import com.irdstudio.efp.loan.service.vo.AccLoanVO;
import com.irdstudio.efp.loan.service.vo.LoanRepayDetailVO;
import com.irdstudio.efp.nls.common.constant.PrdAdaptedSoltEnums;
import com.irdstudio.efp.nls.common.constant.QueueTaskEnums;
import com.irdstudio.efp.nls.common.util.PrdQueueSlotUtils;
import com.irdstudio.efp.nls.service.facade.queue.NlsProcessBizService;
import com.irdstudio.efp.nls.service.facade.yed.YedRepaySoltService;
import com.irdstudio.efp.nls.service.vo.queue.NlsProcessBizVO;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

/**
 * 需求编号：【2019D0509】（业务提的需求编号）
 * 问题编号：【】（若不是因为修复问题而新增该类，则留空）
 * 开发人员：【wanglh4】
 * 创建日期：【2020/7/28 9:56】
 * 功能描述：
 */
@Service("yedRepayQueueHandleService")
@Scope(scopeName = "prototype")
public class YedRepayQueueHandle extends AbstractThreadTask {

    private static Logger logger = LoggerFactory.getLogger(YedRepayQueueHandle.class);

    private LoanRepayDetailVO loanRepayDetailVO;

    public void setLoanRepayDetailVO(LoanRepayDetailVO loanRepayDetailVO) {
        this.loanRepayDetailVO = loanRepayDetailVO;
    }

    @Override
    public ExecuteRtnInfo call() throws Exception {
        this.doTask();
        return new ExecuteRtnInfo(null, null, null, null, true);
    }

    /**
     * 根据申请信息中产品信息查询产品适配插槽表，获取该产品待执行的插槽;<br>
     * 判断该插槽状态，如果是待执行，则执行对应的服务；如果是已挂起的，则重跑；
     */
    @Override
    public void doTask() throws HangException, BizException, Exception {
        String setlSeq = loanRepayDetailVO.getSetlSeq();
        String loanNo = loanRepayDetailVO.getLoanNo();
        TraceUtil.setTraceId(loanRepayDetailVO.getGlobalSerno());
        TraceUtil.setPlafmID(loanRepayDetailVO.getPlatfmId());
        TraceUtil.setPlafmUsrID(loanRepayDetailVO.getPlatfmUsrId());
        ThreadContext.put(TraceUtil.GLBLSRLNO, loanRepayDetailVO.getGlobalSerno());
        logger.info("*********************************************");
        logger.info("*            开始一笔新的执行队列                 * ");
        logger.info("*********************************************");
        logger.info("* 申请流水号:【" + setlSeq + "】*");
        logger.info("* 借据号 : 【" + loanNo + "】  *");
        logger.info("* 客户名称 : 【" + loanRepayDetailVO.getCustName() + "】  *");
        logger.info("* 全局流水号 : 【" + loanRepayDetailVO.getGlobalSerno() + "】  *");
        logger.info("* 创建日期 : 【" + loanRepayDetailVO.getSetlCreateDt() + "】*");
        logger.info("*********************************************");
        try {
            logger.info("根据借据号【" + loanNo + "】查询借据信息开始----------------");
            AccLoanVO accLoanVO = new AccLoanVO();
            accLoanVO.setBillNo(loanNo);
            accLoanVO = accLoanService.queryByPk(accLoanVO);
            if(Objects.isNull(accLoanVO)) {
                throw new BizException("根据借据号【" + loanNo + "】，查询到的借据信息为空！");
            }
            logger.info("根据借据号【" + loanNo + "】查询借据信息结束----------------查询结果为："+ JSON.toJSONString(accLoanVO));

            logger.info("根据产品ID和插槽状态查询产品插槽信息");
            PrdAdaptedSoltVO prdAdaptedSoltVo = new PrdAdaptedSoltVO();
            prdAdaptedSoltVo.setPrdId(accLoanVO.getPrdId());
            prdAdaptedSoltVo.setSoltState(PrdAdaptedSoltEnums.SlotStateEnum.ON.getCode());// 插槽状态
            prdAdaptedSoltVo.setSoltStage(loanRepayDetailVO.getSoltStage()); // 插槽阶段
            List<PrdAdaptedSoltVO> prdAdaptedSoltVoList = prdAdaptedSoltService.queryByPrdIdAndState(prdAdaptedSoltVo);
            Objects.requireNonNull(prdAdaptedSoltVoList);

            logger.info("根据申请流水号:【" + setlSeq + "】查询网贷业务过程信息");
            NlsProcessBizVO nlsProcessBizVo = new NlsProcessBizVO();
            nlsProcessBizVo.setApplySeq(setlSeq);
            nlsProcessBizVo = nlsProcessBizService.queryByPk(nlsProcessBizVo);
            Objects.requireNonNull(nlsProcessBizVo);
            /************************** 性能调优迁移代码 author：wangyk date:20190219 start******************************/
            Method method = null;
            for (int i = 0; i < prdAdaptedSoltVoList.size(); i++) {
                PrdAdaptedSoltVO prdAdaptedSolt = prdAdaptedSoltVoList.get(i);
                String prdId = prdAdaptedSolt.getPrdId();
                String soltCode = prdAdaptedSolt.getSoltCode();
                String soltName = prdAdaptedSolt.getSoltName();
                String soltService = prdAdaptedSolt.getSoltService();// 插槽服务类，此处指注册到dubbo中的实现类
                String soltStage = prdAdaptedSolt.getSoltStage();
                int soltOrder = prdAdaptedSolt.getSoltOrder();
                // 校验产品插槽中的产品ID、插槽代码、插槽名称、插槽服务和顺序是否正确，不正确则抛出异常
                boolean checkFlag = PrdQueueSlotUtils.checkAdaptedSoltInfo(prdId, soltCode, soltName, soltService, soltStage, soltOrder);
                if (!checkFlag) {
                    logger.error("产品插槽中的产品ID【" + prdId + "】插槽代码【" + soltCode + "】插槽名称【" + soltName + "】插槽服务【" + soltService + "】和顺序【" + soltOrder + "】错误，请检查.");
                    throw new Exception("产品插槽中的产品ID【" + prdId + "】插槽代码【" + soltCode + "】插槽名称【" + soltName + "】插槽服务【" + soltService + "】和顺序【" + soltOrder + "】错误，请检查.");
                }
                /** 更新执行状态为待执行 **/
                method = nlsProcessBizVo.getClass().getMethod("setSoltState" + soltOrder, String.class);
                method.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.EXECUTORY.getCode());
            }
            nlsProcessBizService.updateByPk(nlsProcessBizVo);
            /************************** 性能调优迁移代码 author：wangyk date:20190219 end ******************************/

            logger.info("申请流水号:【" + setlSeq + "】的遍历产品插槽的信息开始");
            for (int i = 0; i < prdAdaptedSoltVoList.size(); i++) {
                PrdAdaptedSoltVO prdAdaptedSolt = prdAdaptedSoltVoList.get(i);
                String prdId = prdAdaptedSolt.getPrdId();
                String soltCode = prdAdaptedSolt.getSoltCode();
                String soltName = prdAdaptedSolt.getSoltName();
                String soltService = prdAdaptedSolt.getSoltService();// 插槽服务类，此处指注册到dubbo中的实现类
                String soltStage = prdAdaptedSolt.getSoltStage();
                int soltOrder = prdAdaptedSolt.getSoltOrder();
                // 校验产品插槽中的产品ID、插槽代码、插槽名称、插槽服务和顺序是否正确，不正确则抛出异常
                boolean checkFlag = PrdQueueSlotUtils.checkAdaptedSoltInfo(prdId, soltCode, soltName, soltService, soltStage, soltOrder);
                if (!checkFlag) {
                    logger.error("产品插槽中的产品ID【" + prdId + "】插槽代码【" + soltCode + "】插槽名称【" + soltName + "】插槽服务【" + soltService + "】和顺序【" + soltOrder + "】错误，请检查.");
                    throw new Exception("产品插槽中的产品ID【" + prdId + "】插槽代码【" + soltCode + "】插槽名称【" + soltName + "】插槽服务【" + soltService + "】和顺序【" + soltOrder + "】错误，请检查.");
                }

                // 根据插槽代码 映射
                Method methodSoltName = nlsProcessBizVo.getClass().getMethod("setSoltName" + soltOrder, String.class);
                Method methodSoltTime = nlsProcessBizVo.getClass().getMethod("setSoltTime" + soltOrder, String.class);

                nlsProcessBizVo.setSoltCode(soltCode);// 插槽代码
                nlsProcessBizVo.setSoltName(soltName);// 插槽名称
                nlsProcessBizVo.setSoltService(soltService);// 插槽服务类
                nlsProcessBizVo.setExceptionFlag(Boolean.FALSE);// 异常标志
                nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.EXECUTING.getCode());
                nlsProcessBizService.updateByPk(nlsProcessBizVo);

                // 更新当前插槽代码、当前插槽名称
                /** 当前插槽代码 */
                loanRepayDetailVO.setSoltCode(soltCode);
                /** 当前插槽名称 */
                loanRepayDetailVO.setSoltName(soltName);
                /** 插槽名称 */
                methodSoltName.invoke(nlsProcessBizVo, soltName);
                /** 插槽开始时间 */
                methodSoltTime.invoke(nlsProcessBizVo, DateUtility.getCurrAppDateTimeString());

                /**
                 * 【变更原因】 mycat中不允许修改作为分库依据的列，所以更新的时候去掉“loan_no”
                 * 【变更操作】将更新执行的bean中loanNo字段赋值为null，更新语句会检测是否为空，再做更新。
                 * @date 2019/06/15
                 * @author qiuyf
                 *
                 * 【2019/06/17，需求要求不使用mycat分片】
                 * loanRepayDetailVO.setLoanNo(null);
                 */

                /** 更新申请进件信息 */
                loanRepayDetailService.updateByPk(loanRepayDetailVO);

                logger.info("根据插槽服务 ：【" + soltService + "】映射处理Bean开始");
                YedRepaySoltService slotServiceBean = (YedRepaySoltService) SpringContextUtils.getBean(soltService);
                logger.info("根据插槽服务 ：【" + soltService + "】映射处理Bean结束，该对象为：【" + slotServiceBean + "】，调用对应的服务开始。");
                /**
                 * 【变更原因】前面为了更新loanRepayDetailVO成功，将loanRepayDetailVO里面字段loanNo设置为空了，导致在插槽处理需要到loanNo的地方，从loanRepayDetailVO里面取到空值，从而出错
                 * 【变更操作】在执行插槽前，给loanNo重新赋值
                 * @date 2019/06/15
                 * @author qiuyf
                 */
                loanRepayDetailVO.setLoanNo(loanNo);
                slotServiceBean.execute(nlsProcessBizVo, loanRepayDetailVO, soltOrder);
                logger.info("根据插槽服务 ：【" + soltService + "】映射处理Bean结束，该对象为：【" + slotServiceBean + "】，调用对应的服务结束。");
            }
            logger.info("申请流水号:【" + setlSeq + "】的遍历产品插槽的信息结束");

            logger.info("根据申请流水号:【" + setlSeq + "】更新业务流程表队列任务状态为执行成功开始");
            nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.SUCCESS.getCode());

            logger.info("根据申请流水号:【" + setlSeq + "】更新网贷任务队列中队列任务状态为执行成功开始");
            /** 设置插槽结果信息 **/
            loanRepayDetailVO.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
            /** 设置申请状态信息为通过 **/
            loanRepayDetailVO.setWfApprSts(MsLoanConstant.NlsApplyStateEnum.PASS.getVALUE());
            /** 设置申请最近修改时间 **/
            loanRepayDetailVO.setLastChgDt(DateUtility.getCurrAppDateTimeString());// 最近修改时间

            /**
             * 【变更原因】 mycat中不允许修改作为分库依据的列，所以更新的时候去掉“loan_no”
             * 【变更操作】将更新执行的bean中loanNo字段赋值为null，更新语句会检测是否为空，再做更新。
             * @date 2019/06/15
             * @author qiuyf
             *
             * 【2019/06/17，需求要求不使用mycat分片】
             * loanRepayDetailVO.setLoanNo(null);
             */
            loanRepayDetailService.updateByPk(loanRepayDetailVO);
            logger.info("根据申请流水号:【" + setlSeq + "】更新 申请进件信息中申请状态为通过结束");

            nlsProcessBizService.updateByPk(nlsProcessBizVo);
            logger.info("根据申请流水号:【" + setlSeq + "】更新业务流程表队列任务状态为执行成功结束");
        } catch (HangException e) {
            logger.error("队列处理服务中申请流水号:【" + setlSeq + "】的挂起异常信息为：" + e.getMessage());
            throw e;
        } catch (BizException e) {
            logger.error("队列处理服务中申请流水号:【" + setlSeq + "】的业务异常信息为：" + e.getMessage());
            throw e;
        } catch (Exception e) {
            logger.error("队列处理服务中申请流水号:【" + setlSeq + "】的其他异常信息为：" + e.getMessage());
            throw e;
        } finally {
            TraceUtil.clear();
        }
    }

    @Autowired
    @Qualifier("prdAdaptedSoltService")
    private PrdAdaptedSoltService prdAdaptedSoltService;
    @Autowired
    @Qualifier("nlsProcessBizService")
    private NlsProcessBizService nlsProcessBizService;
    @Autowired
    @Qualifier("loanRepayDetailService")
    private LoanRepayDetailService loanRepayDetailService;
    @Autowired
    @Qualifier("accLoanService")
    private AccLoanService accLoanService;

}
